#include <algorithm>
#include <iterator>
#include <string>
#include <cctype>
#include "Person.h"
#include "PersonType.h"
#include "GenealogyTree.h"
#include "conf.h"
#include "common/FindAll.h"
#include "tree/TreeSerializer.h"
#include "tree/TreeIterator.h"

namespace etu {

using namespace etu::common;
using namespace std;
 std::string::iterator StrIterator;

inline bool caseInsCharCompSingle(char a, char b)
{
  return(std::tolower(a) == b);
}

std::string::const_iterator caseInsFind(string& s, string& p)
{
   //std::locale locRus("ru_RU.UTF-8");

   //std::transform(p.begin(), p.end(), p.begin(),
   //               std::bind1st(std::mem_fun(&std::ctype<char>::tolower), &std::use_facet<std::ctype<char> >(locRus)));
   return(search(s.begin(), s.end(), p.begin(), p.end()));//, caseInsCharCompSingle));
}

bool find(const etu::genealogy_tree::Person* first, const etu::genealogy_tree::Person* second)
{
    string& firstStr = const_cast<string&> (first->getDescription());
    string& secondStr = const_cast<string&> (second->getDescription());
	std::string::const_iterator it = caseInsFind(firstStr, secondStr);

	if (((first->getFirstName() == second->getFirstName())     || (second->getFirstName().empty())) &&
	    ((first->getLastName() == second->getLastName())       || (second->getLastName().empty())) &&
	    ((first->getBirthDate() == second->getBirthDate())     || Date::isUnkown(second->getBirthDate())) &&
	    ((first->getDeathDate() == second->getDeathDate())     || Date::isUnkown(second->getDeathDate())) &&
	    ((first->getGender() == second->getGender())           || (second->getGender() == etu::genealogy_tree::UNKNOWN_GENDER)) &&
	    ((firstStr.end() != it)                 || (second->getDescription().empty())))
    return true;

	return false;
}

namespace genealogy_tree {

using etu::tree::TreeSerializer;
using etu::tree::TreeIterator;
using etu::common::find_all;

GenealogyTree::GenealogyTree() {
    //
}

GenealogyTree::GenealogyTree(const GenealogyTree& orig) {
    this->tree = orig.tree; //->clone();
}

GenealogyTree::~GenealogyTree() {
    //delete this->tree;
}

void GenealogyTree::setRoot(const Person* root) throw(NullPointerException) {
#ifdef __DEBUG__
    if(root == NULL) {
        throw NullPointerException("Корень дерева не может быть равен NULL");
    }
#endif /* __DEBUG__ */

    this->tree.setRoot(root);
}

const Person* GenealogyTree::getRoot() const throw(GenealogyTreeException) {
    return this->tree.getRoot();
}

bool GenealogyTree::isEmpty() const {
    return this->tree.isEmpty();
}

int GenealogyTree::getSize() const {
    return this->tree.getSize();
}

bool GenealogyTree::contains(const Person* person) const 
    throw(NullPointerException) {
    
    return this->tree.contains(person);
}

bool GenealogyTree::hasMother(const Person* person) const 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Не определены данные матери при её добавлении в дерево");
    }
    
    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит человека, для которого добавляются данные о его матери");
    }
#endif /* __DEBUG__ */

    bool ret = false;
    std::list<const Person *>* tmpParents = this->tree.getParents(person);
    std::list<const Person *>::iterator it;

    for(it = tmpParents->begin(); it != tmpParents->end(); it++) {
        const Person* parent = (* it);

        if(parent->getGender() == FEMALE_GENDER) {
            ret = true;
            break;
        }
    }

    delete tmpParents;

    return ret;
}

bool GenealogyTree::hasFather(const Person* person) const 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Не определены данные отца при её добавлении в дерево");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит человека, для которого добавляются данные о его отце");
    }
#endif /* __DEBUG__ */

    bool ret = false;
    std::list<const Person *>* tmpParents = this->tree.getParents(person);
    std::list<const Person *>::iterator it;

    for(it = tmpParents->begin(); it != tmpParents->end(); it++) {
        const Person* parent = (* it);

        if(parent->getGender() == MALE_GENDER) {
            ret = true;
            break;
        }
    }

    delete tmpParents;

    return ret;
}

const Person* GenealogyTree::getMother(const Person* person) const 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Человек, для которого ищутся данные о его матери, должен быть задан");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого ищутся данные о его матери");
    }
#endif /* __DEBUG__ */

    const Person* ret = NULL;
    std::list<const Person *>* tmpParents = this->tree.getParents(person);
    std::list<const Person *>::iterator it;

    for(it = tmpParents->begin(); it != tmpParents->end(); it++) {
        const Person* parent = (* it);

        if(parent->getGender() == FEMALE_GENDER) {
            ret = parent;
            break;
        }
    }

    delete tmpParents;

#ifdef __DEBUG__
    if(ret == NULL) {
        throw GenealogyTreeException("Для заданного человека не содержатся данные о его матери");
    }
#endif /* __DEBUG__ */

    return ret;
}

const Person* GenealogyTree::getFather(const Person* person) const 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Человек, для которого ищутся данные о его отце, должен быть задан");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого ищутся данные о его отце");
    }
#endif /* __DEBUG__ */

    const Person* ret = NULL;
    std::list<const Person *>* tmpParents = this->tree.getParents(person);
    std::list<const Person *>::iterator it;

    for(it = tmpParents->begin(); it != tmpParents->end(); it++) {
        const Person* parent = (* it);

        if(parent->getGender() == MALE_GENDER) {
            ret = parent;
            break;
        }
    }

    delete tmpParents;

#ifdef __DEBUG__
    if(ret == NULL) {
        throw GenealogyTreeException("Для заданного человека не содержатся данные о его отце");
    }
#endif /* __DEBUG__ */

    return ret;
}

void GenealogyTree::setMother(const Person* person, const Person* mother) 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого задаются данные о его матери");
    }
    
    if(person == NULL) {
        throw NullPointerException("Данные о человеке, для которого задаются данные о его матери, должны быть определены");
    }
    
    if(mother == NULL) {
        throw NullPointerException("Данные о матери должны быть определены");
    }

    if(mother->getGender() != FEMALE_GENDER) {
        throw GenealogyTreeException("Пол матери человека должен быть женским");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого задаются данные о его матери");
    }

    if(person->getBirthDate() < mother->getBirthDate()) {
        throw GenealogyTreeException("Человек, для которого задаются данные о его матери, не может родиться раньше, чем его мать");
    }

    if(!Date::isUnkown(mother->getDeathDate())
            && (mother->getDeathDate() < person->getBirthDate())) {
        throw GenealogyTreeException("Мать человека не может умереть раньше, чем родиться заданный человек");
    }
#endif /* __DEBUG__ */

    if(this->hasMother(person)) {
        this->tree.remove(mother);
    }

    this->tree.addParent(person, mother);
}

void GenealogyTree::setFather(const Person* person, const Person* father) 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого задаются данные о его отце");
    }
    
    if(person == NULL) {
        throw NullPointerException("Данные о человеке, для которого задаются данные о его отце, не определены");
    }
    
    if(father == NULL) {
        throw NullPointerException("Данные о матери должны быть определены");
    }

    if(father->getGender() != MALE_GENDER) {
        throw NullPointerException("Отец человека должен иметь мужской пол");
    }

    if(person->getBirthDate() < father->getBirthDate()) {
        throw GenealogyTreeException("Человек не может родиться раньше, чем его отец");
    }
#endif /* __DEBUG__ */

    if(this->hasFather(person)) {
        this->tree.remove(father);
    }

    this->tree.addParent(person, father);
}

bool GenealogyTree::isMatrimony(const Person* firstPerson, const Person* secondPerson) const
throw(GenealogyTreeException, NullPointerException) {
#ifdef __DEBUG__
    if(firstPerson == NULL)
        throw NullPointerException("Данные об обоих людях должны быть определены");

    if(!this->tree.contains(firstPerson))
        throw GenealogyTreeException("Дерево должно содержать данные об обоих людях");

    if(secondPerson == NULL)
        throw NullPointerException("Данные об обоих людях должны быть определены");

    if(!this->tree.contains(secondPerson))
        throw GenealogyTreeException("Дерево должно содержать данные об обоих людях");
#endif /* __DEBUG__ */

	std::list<const Person *>* tmp1Childs = this->tree.getChilds(firstPerson);
    std::list<const Person *>* tmp2Childs = this->tree.getChilds(secondPerson);

    std::list<const Person *>::iterator it;
    bool ret = false;

    for(it = tmp1Childs->begin(); it != tmp1Childs->end(); it++)
      if (tmp2Childs->end() != find(tmp2Childs->begin(), tmp2Childs->end(), *it))
      {
    	ret = true;
        break;
      }

    delete tmp1Childs;
    delete tmp2Childs;

    return ret;
}

bool GenealogyTree::hasChilds(const Person* person) const 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Данные о человеке должны быть заданы");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данные о человеке, для которого ищутся его потомки");
    }
#endif /* __DEBUG__ */

    return this->tree.hasChilds(person);
}

void GenealogyTree::addChild(const Person* parent, const Person* child) 
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(parent == NULL) {
        throw NullPointerException("Данные о родителе не определены");
    }
    
    if(child == NULL) {
        throw NullPointerException("Данные о потомке не определены");
    }

    if(!this->tree.contains(parent)) {
        throw GenealogyTreeException("Дерево не содержит данных о родителе");
    }

    if(child->getGender() == UNKNOWN_GENDER) {
        throw GenealogyTreeException("Потомок должен иметь либо женский, либо мужской пол");
    }

    if(child->getBirthDate() < parent->getBirthDate()) {
        throw GenealogyTreeException("Потомок не может родиться раньше, чем его родитель");
    }
#endif /* __DEBUG__ */

    this->tree.addChild(parent, child);
}

void GenealogyTree::remove(const Person* person)
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Данные о человеке не заданы");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данных о заданном человеке");
    }
#endif /* __DEBUG__ */

    this->tree.remove(person);
}

std::list<const Person *>* GenealogyTree::getChilds(const Person* person) const
    throw(GenealogyTreeException, NullPointerException) {

#ifdef __DEBUG__
    if(person == NULL) {
        throw NullPointerException("Данные о человеке должны быть заданы");
    }

    if(!this->tree.contains(person)) {
        throw GenealogyTreeException("Дерево не содержит данных о заданном человеке");
    }
#endif /* __DEBUG__ */

    return this->tree.getChilds(person);
}

TreeIterator<const Person* >* GenealogyTree::getIterator()
{
  return tree.getIterator();
}

int GenealogyTree::getState(const Person* person)
	throw(GenealogyTreeException, NullPointerException)
{
  int state = 0;

  state |= hasMother(person) ? BIT(MOTHER) : 0;
  state |= hasFather(person) ? BIT(FATHER) : 0;
  state |= (getRoot() == person) ? BIT(ROOT) : 0;

  return state;
}

int GenealogyTree::getState(const Person* first, const Person* second)
	throw(GenealogyTreeException, NullPointerException)
{
  int state = 0;
  std::list<const Person *>* childs = getChilds(first);

  state |= (hasFather(first) && (getFather(first) == second)) ? BIT(FATHER) : 0;
  state |= (hasMother(first) && (getMother(first) == second)) ? BIT(MOTHER) : 0;
  state |= (std::find(childs->begin(),childs->end(),second) != childs->end()) ? BIT(CHILD) : 0;
  state |= isMatrimony(first,second) ? BIT(MARRIED) : 0;

  //delete childs;

  return state;
}

GenealogyTree* GenealogyTree::getSibs(const Person* person)
{
  GenealogyTree* ret = new GenealogyTree();
  ret->tree = *tree.getSibs(person);

  return ret;
}

std::ostream& operator<<(std::ostream& os, const GenealogyTree& tree) {
    os << tree.tree;
    return os;
}

std::istream& operator>>(std::istream& is, GenealogyTree& tree) {
    std::string src;

    std::getline(is, src); // TODO

    TreeSerializer<const Person *> serializer;
    Tree<const Person *>* _tree = serializer.deserialize(src);

    tree.tree = (* _tree);

    return is;
}

std::list<const Person *>* GenealogyTree::findAll(const Person* person) const
{
	return find_all( tree.getIterator(), person, find);
}

}

namespace tree {
    using etu::genealogy_tree::Person;
    
    template<>
    const Person* TreeSerializer<const Person*>::defaultParameterValue() {
        return new Person();
    }
}

}
